﻿using System.Data;
using System.Linq;
using System.Modules.Contracts.Attributes;
using System.Modules.SQL.Contracts.Attributes;
using System.Text;

namespace System.Modules.SQL.Extensions
{
    /// <summary>
    /// 
    /// </summary>
    public static class DataRowExtensions
    {
        /// <summary>
        /// This method Checks if the column exists.
        /// </summary>
        /// <param name="row"></param>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public static bool ColumnExists(this DataRow row, string columnName)
        {
            return row.Table.Columns.Contains(columnName);
        }

        /// <summary>
        /// This method checks if the column exists and the value is not null.
        /// </summary>
        /// <param name="row"></param>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public static bool IsValid(this DataRow row, string columnName)
        {
            return row.ColumnExists(columnName) && !row.IsNull(columnName);
        }

        /// <summary>
        /// This Method Loads data row into an object class.
        /// Root data object class should be implementing IBaseDataObject in order to Load complex properties recursively.
        /// </summary>
        /// <param name="row"></param>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TBaseObject"> </typeparam>
        /// <returns></returns>
        public static T Load<TBaseObject, T>(this DataRow row)
        {
            return (T)row.Load<TBaseObject>(typeof(T), false);
        }

        /// <summary>
        /// This Method Loads data row into an object class.
        /// Root data object class should be implementing IBaseDataObject in order to Load complex properties recursively.
        /// </summary>
        private static object Load<TBaseObject>(this DataRow row, Type type, bool appendTypeName)
        {
            var obj = Activator.CreateInstance(type);
            if (row != null)
            {
                var properties = type.GetProperties();
                foreach (var property in properties)
                {
                    if (property.PropertyType.IsSubclassOf(typeof(TBaseObject)))
                    {
                        property.SetValue(obj, row.Load<TBaseObject>(property.PropertyType, true), null);
                    }
                    else
                    {
                        var propertyNameBuilder = new StringBuilder();
                        if (appendTypeName)
                        {
                            propertyNameBuilder.Append(type.Name);
                            propertyNameBuilder.Append("_");
                        }
                        propertyNameBuilder.Append(property.Name);
                        var propertyName = propertyNameBuilder.ToString();
                        var encodeProperty = property.GetCustomAttributes(typeof(EncodeProperty), true).FirstOrDefault() as EncodeProperty;
                        var bindingProperty = property.GetCustomAttributes(typeof(BindingDBField), true).FirstOrDefault() as BindingDBField;
                        if (bindingProperty != null && !String.IsNullOrEmpty(bindingProperty.Name))
                        {
                            propertyName = bindingProperty.Name;
                        }
                        if (row.IsValid(propertyName) && property.CanWrite)
                        {
                            var fieldValue = row[propertyName];
                            if (bindingProperty != null && bindingProperty.Function != null)
                            {
                                fieldValue = bindingProperty.Function.Invoke(fieldValue);
                            }
                            if (fieldValue != null)
                            {
                                if (property.PropertyType == typeof(String) && encodeProperty != null)
                                {
                                    fieldValue = encodeProperty.Encode(fieldValue.ToString());
                                }
                                property.SetValue(obj, fieldValue.GetType() == property.PropertyType ? fieldValue : Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType), null);
                            }
                        }
                    }
                }
            }
            return obj;
        }
    }
}
